#pragma once

#include <linux/types/wait_queue.h>
#include <linux/sched/sched.h>

/* wait_queue_entry::flags */
#define WQ_FLAG_EXCLUSIVE 0x01
#define WQ_FLAG_WOKEN 0x02
#define WQ_FLAG_CUSTOM 0x04
#define WQ_FLAG_DONE 0x08
#define WQ_FLAG_PRIORITY 0x10

/*
 * Macros for declaration and initialisaton of the datatypes
 */

#define __WAITQUEUE_INITIALIZER(name, tsk) \
	{                                      \
		.private = tsk,                    \
		.func = default_wake_function,     \
		.entry = { NULL,                   \
				   NULL }                  \
	}

#define DECLARE_WAITQUEUE(name, tsk) \
	struct wait_queue_entry name = __WAITQUEUE_INITIALIZER(name, tsk)

#define DECLARE_WAIT_QUEUE_HEAD(name) \
	struct wait_queue_head name

#define DECLARE_WAIT_QUEUE_HEAD_ONSTACK(name) DECLARE_WAIT_QUEUE_HEAD(name)

int default_wake_function(struct wait_queue_entry *wq_entry, unsigned mode, int flags, uintptr_t key);
int woken_wake_function(struct wait_queue_entry *wq_entry, unsigned mode, int sync, uintptr_t key);

int __wake_up(struct wait_queue_head *wq_head,
			  unsigned int mode, int nr, uintptr_t key);

void __init_waitqueue_head(struct wait_queue_head *wq_head, const char *name,
						   struct lock_class_key *key);

void __init_wait_func(struct wait_queue_entry *wq_entry, wait_queue_func_t func);
void remove_wait_queue(struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry);
long wait_woken(struct wait_queue_entry *wq_entry, unsigned mode, long timeout);

#define init_waitqueue_head(wq_head)                        \
	do                                                      \
	{                                                       \
		static struct lock_class_key __key;                 \
                                                            \
		__init_waitqueue_head((wq_head), #wq_head, &__key); \
	} while (0)

#define wait_woken_interruptible(wq_entry, condition)                               \
	({                                                                              \
		int __ret = 0;                                                              \
		if (!(condition))                                                           \
			__ret = wait_woken(wq_entry, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT); \
		__ret;                                                                      \
	})

#define wake_up_interruptible_poll(x, m) \
	__wake_up(x, TASK_INTERRUPTIBLE, 1, m)

extern void add_wait_queue(struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry);
